package mylab.utils.common.concurrent;

import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListenableScheduledFuture;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import lombok.experimental.UtilityClass;

@UtilityClass
public class RunnableUtil implements Concurrent {

    /**
     * 执行异步任务并等待执行完成
     */
    public void runSync(Runnable runnable) throws ExecutionException, InterruptedException {
        ListenableFuture<?> future = submit(runnable);
        future.get();
    }

    /**
     * 执行异步任务并等待执行完成
     */
    public void runSync(Runnable runnable, long timeout, TimeUnit unit) throws ExecutionException, InterruptedException, TimeoutException {
        ListenableFuture<?> future = submit(runnable);
        future.get(timeout, unit);
    }

    /**
     * 执行异步任务并等待执行完成
     */
    @SuppressWarnings("all")
    public void runSync(Runnable runnable, int tryTimes) throws ExecutionException, InterruptedException {
        Preconditions.checkState(tryTimes > 0);
        Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder()
                .retryIfException()
                .withWaitStrategy(WaitStrategies.noWait())
                .withStopStrategy(StopStrategies.stopAfterAttempt(tryTimes))
                .build();
        runSync(runnable, retryer);
    }

    /**
     * 执行异步任务并等待执行完成
     */
    @SuppressWarnings("all")
    public void runSync(Runnable runnable, Retryer<Void> retryer) throws ExecutionException, InterruptedException {
        Preconditions.checkNotNull(runnable);
        Preconditions.checkNotNull(retryer);
        Callable<Void> callable = () -> {
            runnable.run();
            return null;
        };

        ListenableFuture<?> future = listeningExecutor.submit(() -> retryer.call(callable));
        future.get();
    }

    /**
     * 执行异步任务,任务无返回值
     *
     * @param runnable 可执行任务
     */
    public ListenableFuture<?> submit(Runnable runnable) {
        Preconditions.checkNotNull(runnable);
        return listeningExecutor.submit(runnable);
    }

    /**
     * 执行异步任务,任务无返回值
     *
     * @param runnable 可执行任务
     * @param callback 异步回调
     */
    public ListenableFuture<?> submit(Runnable runnable, FutureCallback<Object> callback) {
        Preconditions.checkNotNull(runnable);
        ListenableFuture<?> future = listeningExecutor.submit(runnable);
        Futures.addCallback(future, callback, listeningExecutor);
        return future;
    }


    /**
     * 延时执行
     *
     * @param runnable 可执行任务
     * @param delay    延时
     * @param unit     时间单位
     */
    public ListenableScheduledFuture<?> schedule(Runnable runnable, long delay, TimeUnit unit) {
        return listeningScheduledExecutor.schedule(runnable, delay, unit);
    }

    /**
     * 以固定频率执行任务
     *
     * @param runnable     可执行任务
     * @param initialDelay 初始延时
     * @param period       固定频率
     * @param unit         时间单位
     */
    public ListenableScheduledFuture<?> scheduleAtFixedRate(Runnable runnable, long initialDelay, long period, TimeUnit unit) {
        return listeningScheduledExecutor.scheduleAtFixedRate(runnable, initialDelay, period, unit);
    }

    /**
     * 以固定延时执行任务
     *
     * @param runnable     可执行任务
     * @param initialDelay 初始延时
     * @param delay        固定延时
     * @param unit         时间单位
     */
    public ListenableScheduledFuture<?> scheduleWithFixedDelay(Runnable runnable, long initialDelay, long delay, TimeUnit unit) {
        return listeningScheduledExecutor.scheduleWithFixedDelay(runnable, initialDelay, delay, unit);
    }
}
